/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.javanile.me.chessengine;

/**
 *
 * @author cicciodarkast
 */
public class Engine {
    
	private Node	start_node;
    private Node	brain_node;
	
	private Moves	start_line;
    private Moves	brain_line;
	
	private String	error_message;
	private int		error_code;
    
    public Engine() {
		this(Constants.START_POSITION);
    }
    
	public Engine(String fen) {
		Log.line("Init engine: "+fen);
        start_node = new Node(fen);
        start_line = new Moves();
		brain_node = new Node(fen);
		brain_line = new Moves();
    }
    
    public Moves getMoves() {		
		Moves moves = new Moves();
		MovesLoop loop0 = new MovesLoop(getMoves(start_node));
		
		while(loop0.next()) {
			boolean good = true;
			start_node.doMove(loop0.move());
			MovesLoop loop1 = new MovesLoop(getMoves(start_node));
			while(loop1.next()) {
				start_node.doMove(loop1.move());
				if (!start_node.testKings()) {
					good = false;
					//System.out.println(loop0.move().toString()+" "+loop1.move().toString());
					//break;
				}
				start_node.unMove(loop1.move());
			}			
			start_node.unMove(loop0.move());
			if (good) moves.add(loop0.move());
		}
        return moves;
    }
	
    public Moves getMoves(Node node) {   				
		Moves moves = new Moves();
        
		SquaresLoop loop = new SquaresLoop(node);
		
		while(loop.next()) {
			if (node.testPiece()) {
				if (node.testPawn()) {
					if (node.testPawnDouble())		moves.add(node.getMove());					
					if (node.testPawnEnpassant())	moves.add(node.getMove());					
					if (node.testPawnEstCapture()) {
						if (node.testPromotion()) {
							node.setQueenPromotion();	moves.add(node.getMove());
							node.setRookPromotion();	moves.add(node.getMove());
							node.setBishopPromotion();	moves.add(node.getMove());
							node.setKnightPromotion();	moves.add(node.getMove());
							node.setNotPromotion();
						} else {
							moves.add(node.getMove());
						}
					}
					if (node.testPawnWestCapture())	{
						if (node.testPromotion()) {
							node.setQueenPromotion();	moves.add(node.getMove());
							node.setRookPromotion();	moves.add(node.getMove());
							node.setBishopPromotion();	moves.add(node.getMove());
							node.setKnightPromotion();	moves.add(node.getMove());
							node.setNotPromotion();
						} else {
							moves.add(node.getMove());
						}
					}
					if (node.testPawnSimple())	{
						if (node.testPromotion()) {
							node.setQueenPromotion();	moves.add(node.getMove());
							node.setRookPromotion();	moves.add(node.getMove());
							node.setBishopPromotion();	moves.add(node.getMove());
							node.setKnightPromotion();	moves.add(node.getMove());
							node.setNotPromotion();
						} else {
							moves.add(node.getMove());
						}
					}					
				} else if (node.testLongPiece()) {
					while(node.nextAbility()) {
						//System.out.println(" - "+node.getAbilityInfo());					
						while(node.nextLong()) {
							//System.out.println("    - "+node.getLongInfo());	
							if (node.testLong())	moves.add(node.getMove());
						}					
					}
				} else if (node.testHopePiece()) {					
					if (node.testCastling()) {
						if (node.testLongCastling())	moves.add(node.getMove());
						if (node.testShortCastling())	moves.add(node.getMove());
					}				
					while(node.nextHope()) {
						if (node.testHope()) {
							//System.out.println(" - "+node.getHopeInfo());											
							moves.add(node.getMove());
						}
					}					
				}					
			}			
		}
        return moves;
    }
    private Moves brain_getMoves() {
        return getMoves(brain_node);
    }
    
    public void doMove(Node n, Move m) {
        n.doMove(m);
    }
    public void doMove(Move m) {
		Log.line("Engine doMove "+m.toString());
		Moves legals = getMoves();
		Log.line("legals are: "+legals.toString());
		if (legals.setSelected(m)) {	
			m = legals.getSelected();
			Log.line("doMove ok: "+m.toString());
			start_line.move(m);
			doMove(start_node,m);
		} else {
			start_node.dump();		
			setError(100,m.toString()+" not valid move");
			Log.line("doMove error: "+m.toString());
			System.out.println(m.toString());
			legals.dump();			
		}
    }
    public void doMove(String m) {
        doMove(new Move(m));
    }
    
    public void unMove(Node n, Move m) {   
		n.unMove(m);
    }
    public void unMove() {
		unMove(start_node,start_line.last());
		start_line.undo();
    }
    
    private void brain_doMove(Move m) {
        doMove(brain_node,m);
    }
    private void brain_unMove(Move m) {
        unMove(brain_node,m);
    }
    
    private int getValue(Node node) {
        return node.getValue();		
    }
	public int getValue() {
		return getValue(start_node);
	}
    
    public int brain_getValue() {
        return getValue(brain_node);
    }
    
	public String getBest() {
		Moves moves = getMoves();
		return moves.at(0).toString();
	}
        
	public void search(int deep) {
		Search search = new Search(deep);
        search.start();
	}
	class Search extends Thread {
		private int deep;
		private Moves line;        
		private int count;
		public Search(int d) {
			deep = d;
			count = 0;
		}
		
		@Override
		public void run() {
			line = new Moves();
			count=0;
			brain_node.set(start_node);
			brain_node.dump();						
			main(deep,-9999,9999);                
        }
		
		private void main(int deep, int alfa, int beta) {
			Moves m = brain_getMoves();
			for(int i=0;i<m.size();i++) {
				System.out.println("Enter: "+m.at(i).toString());
				
				brain_doMove(m.at(i));             
				line.move(m.at(i));

				int score = -analize_loop(deep-1,-beta,-alfa);

				line.undo();
				brain_unMove(m.at(i));			
				
				System.out.println("Exit: "+score);				
			}
		}
		
        private int analize_loop(int deep, int alfa, int beta) {            
            count++;
            //System.out.println("("+deep+" "+alfa+" "+beta+") "+line.toOnelineString());      			
            if (count<100000) {
                if (deep>0) {            
					
					Moves m = brain_getMoves();
                    for(int i=0;i<m.size();i++) {
						
                        brain_doMove(m.at(i));             
                        line.move(m.at(i));
						
                        int node = -analize_loop(deep-1,-beta,-alfa);
					
						line.undo();
                        brain_unMove(m.at(i));
						
						if (node>=beta) {
							return beta;
						}
						if (node> alfa) {
							alfa = node;
							//System.out.println("("+deep+" "+alfa+" "+beta+") "+line.toOnelineString());      			
						}
                    }                
					return alfa;
                }
				return brain_getValue();
            }
			return brain_getValue();
        }
	}
    
	public int getPerft(int deep) {
		if (deep>0) {
			Moves ms = getMoves();
			int nodes = 0;			
			for(int i=0;i<ms.size();i++) {
				doMove(ms.at(i));			
				if (error_code>0) {
					//System.out.println(start_line.toOnelineString());
					//System.out.println(error_message);
					//start_node.dump();
					//System.exit(0);
				}
				nodes += getPerft(deep-1);				
				unMove();
			}
			return nodes;
		}
		return 1;
	}
    
	private void setError(int code, String message) {
		error_code = code;
		error_message = message;
	}
	public String getError() {
		return "["+error_code+"] "+error_message;
	}
	
	public Board getBoard() {
		return start_node.getBoard();
	}
	
	public char getPieceAt(int s) {
		return start_node.getPieceAt(s);
	}
	
    public String toString() {
		String output = "";
		output += start_node.toString();
		output += "T: "+start_node.getTurn()+"\n";        
		output += "V: "+getValue()+"\n";
        output += "E: "+getError()+"\n";
		output += "M: "+getMoves().toString()+"\n";
        return output;
    }
	public void dump() {
		System.out.println(this.toString());
	}
}
